﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameObjectPool : Singleton<GameObjectPool> {

	private class stDelayRecycle
	{
		public GameObject recycleObj;

		public int recycleTime;

		public GameObjectPool.OnDelayRecycleDelegate callback;
	}

	public class ParticleSystemCache
	{
		public ParticleSystem par;

		public bool emmitState;
	}

	public delegate void OnDelayRecycleDelegate(GameObject recycleObj);

	private GameObject m_poolRoot;

	private static int s_frameCounter;

	private bool m_clearPooledObjects;

	private int m_clearPooledObjectsExecuteFrame;

	private LinkedList<GameObjectPool.stDelayRecycle> m_delayRecycle = new LinkedList<GameObjectPool.stDelayRecycle>();

	private Dictionary<int, Queue<PooledGameObject>> m_pooledGameObjectMap = new Dictionary<int, Queue<PooledGameObject>>();

	private Dictionary<int, Component> m_componentMap = new Dictionary<int, Component>();

	public override void Init ()
	{
		base.Init ();
		this.m_poolRoot = new GameObject("GameObjectPool");
		GameObject.DontDestroyOnLoad (this.m_poolRoot);
	}

	public void Update() {
		GameObjectPool.s_frameCounter++;
		this.UpdateDelayRecycle();
		if (this.m_clearPooledObjects && this.m_clearPooledObjectsExecuteFrame == GameObjectPool.s_frameCounter)
		{
			this.ExecuteClearPooledObjects();
			this.m_clearPooledObjects = false;
		}
	}

	public void ClearPooledObjects()
	{
		this.m_clearPooledObjects = true;
		this.m_clearPooledObjectsExecuteFrame = GameObjectPool.s_frameCounter + 1;
	}

	public void ExecuteClearPooledObjects()
	{
		for (LinkedListNode<GameObjectPool.stDelayRecycle> linkedListNode = this.m_delayRecycle.First; linkedListNode != null; linkedListNode = linkedListNode.Next)
		{
			if (null != linkedListNode.Value.recycleObj)
			{
				this.RecycleGameObject(linkedListNode.Value.recycleObj);
			}
		}
		this.m_delayRecycle.Clear();
		this.m_componentMap.Clear();
		Dictionary<int, Queue<PooledGameObject>>.Enumerator enumerator = this.m_pooledGameObjectMap.GetEnumerator();
		while (enumerator.MoveNext())
		{
			KeyValuePair<int, Queue<PooledGameObject>> current = enumerator.Current;
			Queue<PooledGameObject> value = current.Value;
			while (value.Count > 0)
			{
				PooledGameObject cPooledGameObjectScript = value.Dequeue();
				if (cPooledGameObjectScript != null && cPooledGameObjectScript.gameObject != null)
				{
					UnityEngine.Object.Destroy(cPooledGameObjectScript.gameObject);
				}
			}
		}
		this.m_pooledGameObjectMap.Clear();
	}

	private void UpdateDelayRecycle()
	{
		LinkedListNode<GameObjectPool.stDelayRecycle> linkedListNode = this.m_delayRecycle.First;
		int num = (int)(Time.time * 1000f);
		while (linkedListNode != null)
		{
			LinkedListNode<GameObjectPool.stDelayRecycle> linkedListNode2 = linkedListNode;
			linkedListNode = linkedListNode.Next;
			if (null == linkedListNode2.Value.recycleObj)
			{
				this.m_delayRecycle.Remove(linkedListNode2);
			}
			else
			{
				if (linkedListNode2.Value.recycleTime > num)
				{
					break;
				}
				if (linkedListNode2.Value.callback != null)
				{
					linkedListNode2.Value.callback(linkedListNode2.Value.recycleObj);
				}
				this.RecycleGameObject(linkedListNode2.Value.recycleObj);
				this.m_delayRecycle.Remove(linkedListNode2);
			}
		}
	}

	public GameObject GetGameObject(string prefabFullPath, Vector3 pos, Quaternion rot, enResourceType resourceType)
	{
		bool flag = false;
		return this.GetGameObject(prefabFullPath, pos, rot, true, resourceType, out flag);
	}

	public GameObject GetGameObject(string prefabFullPath, Vector3 pos, Quaternion rot, enResourceType resourceType, out bool isInit)
	{
		return this.GetGameObject(prefabFullPath, pos, rot, true, resourceType, out isInit);
	}

	public GameObject GetGameObject(string prefabFullPath, Vector3 pos, enResourceType resourceType)
	{
		bool flag = false;
		return this.GetGameObject(prefabFullPath, pos, Quaternion.identity, false, resourceType, out flag);
	}

	public GameObject GetGameObject(string prefabFullPath, Vector3 pos, enResourceType resourceType, out bool isInit)
	{
		return this.GetGameObject(prefabFullPath, pos, Quaternion.identity, false, resourceType, out isInit);
	}

	public GameObject GetGameObject(string prefabFullPath, enResourceType resourceType)
	{
		bool flag = false;
		return this.GetGameObject(prefabFullPath, Vector3.zero, Quaternion.identity, false, resourceType, out flag);
	}

	public GameObject GetGameObject(string prefabFullPath, enResourceType resourceType, out bool isInit)
	{
		return this.GetGameObject(prefabFullPath, Vector3.zero, Quaternion.identity, false, resourceType, out isInit);
	}

	public T GetCachedComponent<T>(GameObject go, bool autoAdd = false) where T : Component
	{
		if (null == go)
		{
			return (T)((object)null);
		}
		Component component = null;
		if (this.m_componentMap.TryGetValue(go.GetInstanceID(), out component) && (!autoAdd || component != null))
		{
			return component as T;
		}
		component = go.GetComponent<T>();
		if (autoAdd && component == null)
		{
			component = go.AddComponent<T>();
		}
		this.m_componentMap[go.GetInstanceID()] = component;
		if (null == component)
		{
			return null;
		}
		return component as T;
	}

	private GameObject GetGameObject(string prefabFullPath, Vector3 pos, Quaternion rot, bool useRotation, enResourceType resourceType, out bool isInit)
	{
		string text = FileTools.EraseExtension(prefabFullPath);
		Queue<PooledGameObject> queue = null;
		if (!this.m_pooledGameObjectMap.TryGetValue(text.JavaHashCodeIgnoreCase(), out queue))
		{
			queue = new Queue<PooledGameObject>();
			this.m_pooledGameObjectMap.Add(text.JavaHashCodeIgnoreCase(), queue);
		}
		PooledGameObject cPooledGameObjectScript = null;
		while (queue.Count > 0)
		{
			cPooledGameObjectScript = queue.Dequeue();
			if (cPooledGameObjectScript != null && cPooledGameObjectScript.gameObject != null)
			{
				cPooledGameObjectScript.gameObject.transform.SetParent(null, true);
				cPooledGameObjectScript.gameObject.transform.position = pos;
				cPooledGameObjectScript.gameObject.transform.rotation = rot;
				cPooledGameObjectScript.gameObject.transform.localScale = cPooledGameObjectScript.m_defaultScale;
				break;
			}
			cPooledGameObjectScript = null;
		}
		if (cPooledGameObjectScript == null)
		{
			cPooledGameObjectScript = this.CreateGameObject(prefabFullPath, pos, rot, useRotation, resourceType, text);
		}
		if (cPooledGameObjectScript == null)
		{
			isInit = false;
			return null;
		}
		isInit = cPooledGameObjectScript.m_isInit;
		cPooledGameObjectScript.OnGet();
		return cPooledGameObjectScript.gameObject;
	}

    // 预加载资源
    public void PrepareGameObject(string prefabFullPath, enResourceType resourceType, int amount, bool assertNull = true)
    {
        string text = FileTools.EraseExtension(prefabFullPath);
        Queue<PooledGameObject> queue = null;
        if (!this.m_pooledGameObjectMap.TryGetValue(text.JavaHashCodeIgnoreCase(), out queue))
        {
            queue = new Queue<PooledGameObject>();
            this.m_pooledGameObjectMap.Add(text.JavaHashCodeIgnoreCase(), queue);
        }
        if (queue.Count >= amount)
        {
            return;
        }
        amount -= queue.Count;
        for (int i = 0; i < amount; i++)
        {
            PooledGameObject cPooledGameObjectScript = this.CreateGameObject(prefabFullPath, Vector3.zero, Quaternion.identity, false, resourceType, text);
            if (assertNull)
            {
                Debug.AssertFormat(cPooledGameObjectScript != null, "Failed Create Game object from \"{0}\"", new object[]
                {
                    prefabFullPath
                });
            }
            if (cPooledGameObjectScript != null)
            {
                queue.Enqueue(cPooledGameObjectScript);
                cPooledGameObjectScript.gameObject.transform.SetParent(this.m_poolRoot.transform, true);
                cPooledGameObjectScript.OnPrepare();
            }
        }
    }

    // 异步预加载资源
    public void PrepareGameObjectAsync(string prefabFullPath, enResourceType resourceType, int amount, bool assertNull = true)
    {
        string text = FileTools.EraseExtension(prefabFullPath);
        Queue<PooledGameObject> queue = null;
        if (!this.m_pooledGameObjectMap.TryGetValue(text.JavaHashCodeIgnoreCase(), out queue))
        {
            queue = new Queue<PooledGameObject>();
            this.m_pooledGameObjectMap.Add(text.JavaHashCodeIgnoreCase(), queue);
        }
        if (queue.Count >= amount)
        {
            return;
        }
        amount -= queue.Count;
        for (int i = 0; i < amount; i++)
        {
            this.CreateGameObjectAsync(prefabFullPath, Vector3.zero, Quaternion.identity, false, resourceType, text, (cPooledGameObjectScript) => {

                if (assertNull)
                {
                    Debug.AssertFormat(cPooledGameObjectScript != null, "Failed Create Game object from \"{0}\"", new object[]
                    {
                    prefabFullPath
                    });
                }
                if (cPooledGameObjectScript != null)
                {
                    queue.Enqueue(cPooledGameObjectScript);
                    cPooledGameObjectScript.gameObject.transform.SetParent(this.m_poolRoot.transform, true);
                    cPooledGameObjectScript.OnPrepare();
                }

                Debug.Log("pre load obj!path:" + prefabFullPath);
            });
        }
    }

    private PooledGameObject CreateGameObject(string prefabFullPath, Vector3 pos, Quaternion rot, bool useRotation, enResourceType resourceType, string prefabKey)
    {
        bool needCached = false;
        GameObject gameObject = ResourceManager.Instance.GetResource(prefabFullPath, typeof(GameObject), resourceType, needCached, false).m_content as GameObject;
        if (gameObject == null)
        {
            return null;
        }
        GameObject gameObject2;
        if (useRotation)
        {
            gameObject2 = (UnityEngine.Object.Instantiate(gameObject, pos, rot) as GameObject);
        }
        else
        {
            gameObject2 = (UnityEngine.Object.Instantiate(gameObject) as GameObject);
            gameObject2.transform.position = pos;
        }
        Debug.Assert(gameObject2 != null);
        PooledGameObject cPooledGameObjectScript = gameObject2.GetComponent<PooledGameObject>();
        if (cPooledGameObjectScript == null)
        {
            cPooledGameObjectScript = gameObject2.AddComponent<PooledGameObject>();
        }
        cPooledGameObjectScript.Initialize(prefabKey);
        cPooledGameObjectScript.OnCreate();
        return cPooledGameObjectScript;
    }

    private void CreateGameObjectAsync(string prefabFullPath, Vector3 pos, Quaternion rot, bool useRotation, enResourceType resourceType, string prefabKey, System.Action<PooledGameObject> callback)
    {
        ResourceManager.Instance.GetResourceAsync(prefabFullPath, typeof(GameObject), resourceType, (res) => {

            if (res.m_content != null)
            {
                GameObject gameObject = res.m_content as GameObject;

                if (gameObject == null)
                {
                    if (callback != null)
                        callback(null);
                    return;
                }
                GameObject gameObject2;
                if (useRotation)
                {
                    gameObject2 = (UnityEngine.Object.Instantiate(gameObject, pos, rot) as GameObject);
                }
                else
                {
                    gameObject2 = (UnityEngine.Object.Instantiate(gameObject) as GameObject);
                    gameObject2.transform.position = pos;
                }
                Debug.Assert(gameObject2 != null);
                PooledGameObject cPooledGameObjectScript = gameObject2.GetComponent<PooledGameObject>();
                if (cPooledGameObjectScript == null)
                {
                    cPooledGameObjectScript = gameObject2.AddComponent<PooledGameObject>();
                }
                cPooledGameObjectScript.Initialize(prefabKey);
                cPooledGameObjectScript.OnCreate();
                if (callback != null)
                {
                    callback(cPooledGameObjectScript);
                }
            }
        });
    }

    public void RecycleGameObjectDelay(GameObject pooledGameObject, int delayMillSeconds, GameObjectPool.OnDelayRecycleDelegate callback = null)
	{
		GameObjectPool.stDelayRecycle stDelayRecycle = new GameObjectPool.stDelayRecycle();
		stDelayRecycle.recycleObj = pooledGameObject;
		stDelayRecycle.recycleTime = (int)(Time.time * 1000f) + delayMillSeconds;
		stDelayRecycle.callback = callback;
		if (this.m_delayRecycle.Count == 0)
		{
			this.m_delayRecycle.AddLast(stDelayRecycle);
			return;
		}
		//加入列表对应位置
		for (LinkedListNode<GameObjectPool.stDelayRecycle> linkedListNode = this.m_delayRecycle.Last; linkedListNode != null; linkedListNode = linkedListNode.Previous)
		{
			if (linkedListNode.Value.recycleTime < stDelayRecycle.recycleTime)
			{
				this.m_delayRecycle.AddAfter(linkedListNode, stDelayRecycle);
				return;
			}
		}
		this.m_delayRecycle.AddFirst(stDelayRecycle);
	}

	public void RecycleGameObject(GameObject pooledGameObject)
	{
		this._RecycleGameObject(pooledGameObject, false);
	}

	public void RecyclePreparedGameObject(GameObject pooledGameObject)
	{
		this._RecycleGameObject(pooledGameObject, true);
	}

	private void _RecycleGameObject(GameObject pooledGameObject, bool setIsInit)
	{
		if (pooledGameObject == null)
		{
			return;
		}
		PooledGameObject component = pooledGameObject.GetComponent<PooledGameObject>();
		if (component != null)
		{
			Queue<PooledGameObject> queue = null;
			if (this.m_pooledGameObjectMap.TryGetValue(component.m_prefabKey.JavaHashCodeIgnoreCase(), out queue))
			{
				queue.Enqueue(component);
				component.OnRecycle();
				component.transform.SetParent(this.m_poolRoot.transform, true);
				component.m_isInit = setIsInit;
				return;
			}
		}
		UnityEngine.Object.Destroy(pooledGameObject);
	}
}
